
function getCss(curEle, attr) {
  let val = null;
  // 去单位
  let reg = /^(-?\d+(\.\d+)?)([pt|px|em|rem])?$/i;
  if ("getComputedStyle" in window) { // window.getComputedStyle
    val = window.getComputedStyle(curEle, null)[attr];
  } else { // IE 6,7,8
    if (attr === "opacity") {
      reg = /^alphaopacity=(\d+(?:\.\d+)?)$/;
      val = reg.test(val) ? reg.exec(attr)[1] / 100 : 1;
    } else {
      val = curEle.currentStyle[attr];
    }
  }
  return reg.test(val) ? parseFloat(val) : val;
}

function isTouch() {
  return 'ontouchstart' in window ||
    window.DocumentTouch && document instanceof window.DocumentTouch ||
    navigator.maxTouchPoints > 0 ||
    window.navigator.msMaxTouchPoints > 0;
}
const EVENTS = isTouch() ? ["touchstart", "touchmove", "touchend"] : ["mousedown", "mousemove", "mouseup"];

class Watcher {
  constructor() {
    this.events = {};
  }
  on(type, fn) {
    this._getEventInfo(type).push(fn);
    return this;
  }
  trigger(type) {
    const event = this._getEventInfo(type);
    const params = Array.prototype.slice.call(arguments, 1);
    event.forEach(function (fn) {
      fn.apply(fn, params);
    })
    return this;
  }
  _getEventInfo(type) {
    if (!this.events[type]) this.events[type] = [];
    return this.events[type];
  }
  remove(type, fn) {
    const event = this._getEventInfo(type);
    if (!fn) {
      this.events[type] = [];
    } else {
      event.splice(event.indexOf(fn), 1);
    }
    return this;
  }
}

class Drag {
  constructor(el, options) {
    this.$el = el;
    this.watcher = new Watcher();
    this.option = options || {};
    this._data = {};
    this.checkOptions();
    this.init();
  }

  checkOptions() {
    if (typeof this.$el === 'string') {
      this.$el = document.querySelector(this.$el);
    }
    if (this.option.target && typeof this.option.target === 'string') {
      this._data.target = document.querySelector(this.option.target);
    } else {
      this._data.target = this.option.target;
    }
    this.option.target && (this.option.target.style.cursor = 'move');
  }
  isChildren(element, parent) {
    let cur = element;
    for (; cur.parentNode; cur = cur.parentNode) {
      if (cur === parent) {
        return true;
      }
    }
    return false;
  }
  _getEventConfig(ev) {
    return isTouch() ? ev.targetTouches[0] : ev
  }
  init() {
    const that = this;
    const target = this._data.target;
    that._data.dragable = false;
    that.$el.addEventListener(EVENTS[0], function (ev) {
      if (that.isChildren(ev.target, target)) {
        that._data.dragable = true;
      } else {
        that._data.dragable = false;
        return;
      }

      const evConfig = that._getEventConfig(ev);
      const $parent = that.$el.offsetParent;
      const diffX = evConfig.clientX - that.$el.offsetLeft;
      const diffY = evConfig.clientY - that.$el.offsetTop;
      const pDiffX = $parent.offsetLeft || 0;
      const pDiffY = $parent.offsetTop || 0;
      const windowWidth = document.documentElement.clientWidth;
      const windowHeight = document.documentElement.clientHeight;
      const elemWidth = that.$el.offsetWidth;
      const elemHeight = that.$el.offsetHeight;
      const transition = getCss(that.$el, 'transition') || getCss(that.$el, "-webkit-transition") || getCss(that.$el, "-moz-transition");
      const zIndex = getCss(that.$el, 'zIndex');

      function move(e) {
        if (!that._data.dragable) {
          return;
        }

        const eInfo = that._getEventConfig(e);
        let left = eInfo.clientX - diffX;
        let top = eInfo.clientY - diffY;

        if (left + pDiffX < 0) {
          left = left - (left + pDiffX);
        }
        if (top + pDiffY < 0) {
          top = top - (top + pDiffY);
        }
        if (left + pDiffX + elemWidth > windowWidth) {
          left = windowWidth - (pDiffX + elemWidth);
        }
        if (top + pDiffY + elemHeight > windowHeight) {
          top = windowHeight - (pDiffY + elemHeight);
        }

        that.$el.style.position = 'absolute';
        that.$el.style['transition'] = that.$el.style['-webkit-transition'] = that.$el.style['-moz-transition'] = 'unset';
        that.$el.style.left = left + 'px';
        that.$el.style.top = top + 'px';
        that.$el.style.zIndex = 9999999999;

        that.watcher.trigger('move', e, that.$el);
      }
      function end(e) {
        if (!that._data.dragable) {
          return;
        }
        document.removeEventListener(EVENTS[1], move);
        document.removeEventListener(EVENTS[2], end);
        that.$el.style['transition'] = that.$el.style['-webkit-transition'] = that.$el.style['-moz-transition'] = transition;
        that.$el.style.zIndex = zIndex;
        that.watcher.trigger('end', e, that.$el);
      }

      that.watcher.trigger('start', ev, that.$el);
      document.addEventListener(EVENTS[1], move);
      document.addEventListener(EVENTS[2], end);
    });
  }

  on() {
    this.watcher.on.apply(this.watcher, arguments);
    return this.watcher
  }
}